package storage.v1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import com.onpositive.commons.platform.registry.IAdaptable2;
import com.onpositive.dataminer.core.DefaultUniverse;
import com.onpositive.knowledge.model.IThing;
import com.onpositive.knowledge.model.values.ReplacementProposals;
import com.onpositive.knowledge.model.values.ReplacementProposals.Proposal;
import com.onpositive.mediawiki.propertyparser.ParsedCategory;
import com.onpositive.mediawiki.propertyparser.ParsedDocument;
import com.onpositive.mediawiki.propertyparser.PropertyCharacteristic;
import com.onpositive.semantic.model.api.meta.BaseMeta;
import com.onpositive.semantic.model.api.meta.DefaultMetaKeys;
import com.onpositive.semantic.model.api.meta.IHasMeta;
import com.onpositive.semantic.model.api.meta.IMeta;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.IPropertyProvider;
import com.onpositive.semantic.model.api.realm.IRealm;
import com.onpositive.semantic.model.api.realm.IRealmProvider;
import com.onpositive.semantic.model.api.realm.Realm;
import com.onpositive.units.AbstractValueParser;
import com.onpositive.units.ParsedIdent;
import com.onpositive.units.ParsedProperty;
import com.onpositive.units.ParsedSection;
import com.onpositive.units.ParsedSentence;
import com.onpositive.units.ParsedString;
import com.onpositive.units.ParsedValue;
import com.onpositive.units.ParsedValueSequence;
import com.onpositive.units.ParsedValueSequenceV2;
import com.onpositive.units.TextContent;
import com.onpositive.units.ValueCluster;
import com.onpositive.utils.IStringPoolProvider;
import com.onpositive.wikipedialookup.AbstractRefsLookUp2;
import com.onpositive.wikipedialookup.WikipediaRefsLookup;

abstract public class CategoryStorage implements
					  IPropertyProvider,
					  IWikiStorage,
					  IParserIdLookup,
					  IRealmProvider<IThing>,
					  IAdaptable2,
					  ITableInfoContainer {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7054156973962585116L;
	public static final int FORCE_RELOAD_FLAG = 1 << 0;
	public static final int FORCE_REPARSE_FLAG = 1 << 1;
	protected ParsedCategory category;

	public CategoryStorage() {
		super();
	}

	@Override
	public IProperty getProperty(Object obj, String name) {
		
		if(category==null)
			return null;
		
		IProperty special = getSpecialProperty( name) ;
		if( special != null )
			return special ;
		
		PropertyCharacteristic pChar = category.getCharacteristics( name ) ;

		if( pChar == null )
			return null ;
		
		IProperty result = createProperty(pChar);
		if( obj == null )			
			return result ;
		
		ParsedDocument pd = null ;		
		if( obj instanceof String )
		{
			String documentName = (String) obj ;
			pd = category.getParsedDocument( documentName ) ;
		}
		if( obj instanceof ParsedDocument )
			pd = (ParsedDocument) obj ;
		
		if( pd != null && pd.getProperty( name ) != null )
			return result ;
		
		return null;
	}

	@Override
	public Iterable<IProperty> getProperties(Object obj) {
		
		ArrayList<IProperty> result = new ArrayList<IProperty>() ;		
		
		addSpecialProperties( result ) ;
		
		Collection<PropertyCharacteristic> values = category.getChracteristicsMap().values();
		for( PropertyCharacteristic pChar : values ){
//			if( pChar.getCharacteristic( IPropertyConstants.CHAR_PROPERTY_NAME_LABEL ).equals( IPropertyConstants.TEXT_CONTENT_PROPERTY_NAME) )
//				continue ;			
			result.add( createProperty(pChar) ) ;
		}
		return result ;
	}
	
	public IProperty getProperty(String propertyName)
	{
		if( propertyName == null || category == null )
			return null ;
		
		IProperty special = getSpecialProperty(propertyName) ;
		if( special != null )
			return special ;		
		
		PropertyCharacteristic pChar = category.getCharacteristics(propertyName) ;
		return pChar != null ? createProperty(pChar) : null ;
	}	
	
	abstract protected void addSpecialProperties( Collection<IProperty> col ) ;
	abstract protected IProperty getSpecialProperty( String propertyName ) ;
	abstract protected IProperty createProperty( PropertyCharacteristic pChar ) ;

	@Override
	public ParsedDocument getParsedDocument(String id) {
	
		return category.getParsedDocument( id );
	}

	@Override
	public IRealm<IThing> getRealm(IHasMeta model, Object parentObject, Object object) {
		
		Collection<ParsedDocument> docs ;// = category.getDocumentsList() ;
		
//		IUniverse universe = null ;
//		if( parentObject != null )
//		{
//			if( !(parentObject instanceof IUniverse )  )
//				return null ;
//			
//			universe = (IUniverse) parentObject ;
//		}
		
		if( parentObject == null )
			docs = category.getDocumentsList() ;
		
		else{
			if( !( parentObject instanceof String ) )
				return null ;
			
			String categoryName = (String) parentObject ;
		
			if( categoryName.length() == 0 )
				docs = category.getDocumentsList() ;
			else
				docs = category.getSubcategoryDocuments( categoryName ) ;
		}
		Realm<IThing> result = new Realm<IThing>() ;
		for( ParsedDocument pd : docs )
			result.add( createThing( pd.getName() ) ) ;
	
		return result;
	}

	

	public ParsedCategory getCategory() {
		return category;
	}

	public void setCategory(ParsedCategory category) {
		this.category = category;
	}

	@Override
	public String getParserId(String propertyName) {
		
		if( category == null )
			return null ;
		IProperty property = DefaultUniverse.getInstance().getProperty(null, propertyName);
		String stringValue = DefaultMetaKeys.getStringValue(property, PropertyCharacteristic.CHAR_PARSER_ID_LABEL);
		if (stringValue!=null){
			return stringValue;
		}
		PropertyCharacteristic pChar = category.getCharacteristics(propertyName) ;
		if( pChar == null )
			return null ;
		
		return pChar.getCharacteristic( PropertyCharacteristic.CHAR_PARSER_ID_LABEL ) ;
	}	
	AbstractRefsLookUp2 wrl;
	public Object resolveRefs(String id, Object ref) {
		if (wrl==null){
			 wrl= new AbstractRefsLookUp2( category.getRefs(), category.getCaptions() ) ;
		}
		if (ref instanceof ParsedValue){
			ParsedProperty p=new ParsedProperty(id);
			ParsedValue processRefs = processRefs((ParsedValue)ref, null, p, wrl);
			return processRefs;
		}		
		return null;
	}
	
	
	
	public void resolveWikipediaRefs(){
		
		AbstractRefsLookUp2 wrl = new AbstractRefsLookUp2( category.getRefs(), category.getCaptions() ) ;//.getInstance() ;
		
//		ParsedProperty pr = category.getParsedDocument("Curtiss P-6 Hawk").getProperty("caption") ;
//		for( ParsedValue pv : pr.getValues() )
//		{
//			
//			String valueId = pv.getId();
//			if( valueId == ParsedValueSequence.ID || valueId == ParsedSection.ID )
//			{
//				String str = pv.getValueAsString() ;
//				wrl.processString( str, false, "Curtiss P-6 Hawk", pr.getName() ) ;
//			}
//		}
		
		ArrayList<ParsedValue> toDelete = new ArrayList<ParsedValue>() ;
		ArrayList<ParsedValue> toAdd = new ArrayList<ParsedValue>() ;
		for( ParsedDocument pd : category.getDocumentsList() )
		{
			String docName = pd.getName();
			System.out.print( docName +"\n\n") ;
			for( ParsedProperty pp : pd.getProps().values() )
			{
				toDelete.clear() ;
				toAdd.clear() ;
				
				for( ParsedValue pv : pp.getValues() )
				{
					ParsedValue newPv = processRefs( pv, pd, pp, wrl ) ;
					if( newPv == null )
						continue ;
					
					toDelete.add(pv) ;
					toAdd.add(newPv) ;
				}
				for( ParsedValue pv : toDelete  )
					pp.removeValue( pv ) ;
				
				for( ParsedValue pv : toAdd  )
					pp.addValue( pv ) ;				
			}			
		}		
		WikipediaRefsLookup.disposeInstance() ;
	}
	
	public void processRefs(IProperty prop, ParsedValue pv, ReplacementProposals rp) {
		
		if (wrl==null){
			 wrl= new AbstractRefsLookUp2( category.getRefs(), category.getCaptions() ) ;
		}
		
		String valueId = pv.getId();		

		if( valueId == ParsedValueSequenceV2.ID )
		{			
			ParsedValueSequenceV2 pvsv2 = (ParsedValueSequenceV2) pv ;
			ParsedValue[] oldValues = pvsv2.getValuesArray() ;
			for( ParsedValue val : oldValues ){
				processRefs( prop, val, rp) ;
			}
			return;
		}
		else if( valueId.equals( TextContent.ID) ){
			
			return;
//			TextContent tc = (TextContent) pv ;
//			Collection<ParsedSection> oldSections = tc.getSections() ;
//			
//			String docName = pd.getName() ;
//			String propName = pp.getName() ;
//			
//			String newComment = pv.getComment() +" links resolved" ;
//			TextContent newTc = new TextContent(newComment) ;
//			
//			boolean gotChange = false ;
//			for( ParsedSection sec : oldSections ){				
//				
//				String str = (String) sec.value() ;
//				String newStr = wrl.processString( str, valueId == ParsedSection.ID, docName, propName ) ;
//				if( newStr == null || newStr.equals(str) ) 
//					return null ;
//				
//				ParsedSection newPs = new ParsedSection( sec.getName(), sec.getParentSectionName(), newStr, newTc, newComment ) ;
//				newTc.putSection( newPs );
//				gotChange = true ;
//			}
//			if( !gotChange )
//				return null ;
//			
//			return newTc ;
		}
		else 
			processRefsInSimpleValue( prop, pv, rp) ;		
	}
	
	private void processRefsInSimpleValue(IProperty prop, ParsedValue pv, ReplacementProposals rp)
	{
		try{		
			String valueId = pv.getId() ;
			String newComment = pv.getComment() +" links resolved" ;
			String propName = prop.getMeta().getSingleValue( "id", String.class, null ) ;
			
			String oldStr = pv.getContextFragmentString() ;
			oldStr = oldStr.replace("_", " ").trim() ;
			String newStr = wrl.processString( oldStr, valueId == ParsedSection.ID, null, propName ) ;
			if( newStr == null )
				return;
			
			newStr = newStr.replace("_", " ") ;
			if( newStr.equals(oldStr) )
				return;
			
			AbstractValueParser parser = null ;
			if( valueId.equals( ValueCluster.ID ) )
			{
				String valType = pv.getMeta().getSingleValue( ParsedValue.META_KEY_VALUE_TYPE, String.class, null ) ;
				parser = DefaultParserProvider.getInstance().getParser( valType ) ;
			}
			else if( valueId.equals( ParsedIdent.ID ) || valueId.equals( ParsedString.ID ) )
				parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
			else if( valueId.equals( ParsedValueSequence.ID ) )
				parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
			else if( valueId.equals( ParsedSentence.ID ) )
				parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
			else
				return;
			
			HashMap<String,Object> metaInfo = new HashMap<String, Object>() ; 
			
			ParsedValue newVal = parser.parse( newStr, prop.getMeta(), this, metaInfo, newComment) ;
			rp.takeProposal(pv, newVal,1);
			rp.setLooksApplyable(true);
		}
		catch( Exception e ){
			e.printStackTrace() ;
		}
	}

	private ParsedValue processRefs(ParsedValue pv, ParsedDocument pd , ParsedProperty pp, AbstractRefsLookUp2 wrl) {
		
		String valueId = pv.getId();		

		if( valueId == ParsedValueSequenceV2.ID )
		{			
			ParsedValueSequenceV2 pvsv2 = (ParsedValueSequenceV2) pv ;
			ParsedValue[] oldValues = pvsv2.getValuesArray() ;
			ParsedValue[] newValues = new ParsedValue[ oldValues.length ] ;
			int ind = 0 ;
			boolean gotChange = false ;
			for( ParsedValue val : oldValues ){
				ParsedValue newVal = processRefs( val, pd, pp, wrl ) ;
				if( newVal == null )
					newValues[ind++] = val ;
				else{
					newValues[ind++] = newVal ;
					gotChange = true ;
				}
			}
			if( !gotChange )
				return null ;
			
			String newComment = pv.getComment() +" links resolved" ;
			ParsedValueSequenceV2 result = new ParsedValueSequenceV2(newValues, newComment, null ) ;
			return result ;
		}
		else if( valueId.equals( TextContent.ID) ){
			
			return null ;
//			TextContent tc = (TextContent) pv ;
//			Collection<ParsedSection> oldSections = tc.getSections() ;
//			
//			String docName = pd.getName() ;
//			String propName = pp.getName() ;
//			
//			String newComment = pv.getComment() +" links resolved" ;
//			TextContent newTc = new TextContent(newComment) ;
//			
//			boolean gotChange = false ;
//			for( ParsedSection sec : oldSections ){				
//				
//				String str = (String) sec.value() ;
//				String newStr = wrl.processString( str, valueId == ParsedSection.ID, docName, propName ) ;
//				if( newStr == null || newStr.equals(str) ) 
//					return null ;
//				
//				ParsedSection newPs = new ParsedSection( sec.getName(), sec.getParentSectionName(), newStr, newTc, newComment ) ;
//				newTc.putSection( newPs );
//				gotChange = true ;
//			}
//			if( !gotChange )
//				return null ;
//			
//			return newTc ;
		}
		else 
			return processRefsInSimpleValue( pv, pd, pp, wrl ) ;		
	}
	
	public ParsedValue processRefsInSimpleValue(ParsedValue pv, ParsedDocument pd , ParsedProperty pp, AbstractRefsLookUp2 wrl)
	{
		try{
			
		
		String valueId = pv.getId() ;
		String newComment = pv.getComment() +" links resolved" ;
		String docName = pd!=null?pd.getName() :"";
		String propName = pp.getName() ;
		
		String oldStr = pv.getContextFragmentString() ;
		oldStr = oldStr.replace("_", " ").trim() ;
		String newStr = wrl.processString( oldStr, valueId == ParsedSection.ID, docName, propName ) ;
		if( newStr == null )
			return null ;
		
		newStr = newStr.replace("_", " ") ;
		if( newStr.equals(oldStr) )
			return null ;
		
		AbstractValueParser parser = null ;
		if( valueId.equals( ValueCluster.ID ) )
		{
			String valType = pv.getMeta().getSingleValue( ParsedValue.META_KEY_VALUE_TYPE, String.class, null ) ;
			parser = DefaultParserProvider.getInstance().getParser( valType ) ;
		}
		else if( valueId.equals( ParsedIdent.ID ) || valueId.equals( ParsedString.ID ) )
			parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
		else if( valueId.equals( ParsedValueSequence.ID ) )
			parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
		else if( valueId.equals( ParsedSentence.ID ) )
			parser = DefaultParserProvider.getInstance().getParser( ParsedIdent.ID ) ;
		else
			return null ;
		
		HashMap<String,Object> metaInfo = new HashMap<String, Object>() ; 
		IProperty property = this.getProperty(propName);
		
		IMeta meta ;
		if( property != null )
			meta = property.getMeta() ;
		else
			meta = new BaseMeta() ;
		
		ParsedValue newVal = parser.parse( newStr, meta, this, metaInfo, newComment) ;	
		return newVal ;
		}
		catch( Exception e ){
			e.printStackTrace() ;
			return null ;
		}
	}

	@SuppressWarnings("unchecked")
	public <T> T getAdapter(Class<T> adapter)
	{
		if( adapter == IStringPoolProvider.class )
			return (T) category ;
		
		if( adapter == ParsedCategory.class )
			return (T) category ;
		
		if( adapter == IParserIdLookup.class )
			return (T) this ;
		
		if( adapter == IPropertyProvider.class )
			return (T) this ;
		
		if( adapter == ITableInfoContainer.class )
			return (T) this ;
		
		if( adapter == ParsedCategory.class )
			return (T) getCategory() ;
		
//		if( adapter == IStringPoolProvider.class )
//			return (T) this.getCategory() ;
		
		return null ;
	}

	
	abstract protected IThing createThing( Object name ) ;
	
	protected ArrayList<ITableInfo> tableInfoList = new ArrayList<ITableInfo>() ;
	
	protected ArrayList<ITableInfo> getTableInfoList() {
		return tableInfoList;
	}

	@Override
	public void addTableInfo( ITableInfo ti ){
		tableInfoList.add(ti) ;
	}
	
	@Override
	public void clearTableInfo() {
		tableInfoList.clear() ;		
	}
	
	protected void processTableInfo()
	{
		if( tableInfoList.size() == 0 )
			return ;
		try{
			( new BasicTableInfoProcessor( this ) ).processTableInfo() ;			
		}
		finally{
			tableInfoList.clear() ;
		}
	}
	

}